# from my_dbg import *
# Dbg_switch.on(__name__)

import Node_trans_cfg
import ast

class C_maker:
    def __init__(self, module, class_node, type_space):
        self.module = module
        self.class_node = class_node
        self.type_space = type_space
    # def make_func(self, func_node):
    #     t = Node_trans(None, func_node)
    #     return t.trans()
    def func_code(self):
        class_name = self.class_node.name
        class_trans = Node_trans(None, self.class_node)
        class_trans.type_space = self.type_space
        func_list = self.module.func_list(class_name)
        func_code_list = []
        for func_node in func_list:
            if func_node.name == "__init__":
                continue
            func = Node_trans(class_trans, func_node)
            func_code_list.append(func.trans())
            #OOvar(__name__, func_code_list[-1], "func_code")
        func_code_list = "\n\n".join(func_code_list)
        return func_code_list
#         f_name = dir+"/"+class_name+".c"
#         with open(f_name, "w+") as f:
#             OOvar_on(__name__, "C_file", f_name)
#             code = """
# #include <{class_name}.h>

# {func_code_list}
# """.format(**locals())
#             f.write(code)

class Node_trans:
    def __init__(self, father_trans, node):
        self.father = father_trans
        self.node = node
        self.type_space = father_trans.type_space if father_trans != None else None
    
    def show(self, node=None):
        if node== None:
            node = self.node
        OO_info(node.__class__.__name__)
        try:
            OO_info(node.__dict__.items())
        except:
            OO_info(str(node))

    def add_tab(self, s):#缩进
        tab = " "*4
        s = s.splitlines()
        return tab+("\n"+tab).join(s)
    def class_node(self):
        cur_trans = self
        while 1:
            cur_trans = cur_trans.father
            if cur_trans == None:
                return str(None)
            else:
                pass
            cur_node = cur_trans.node
            if cur_node.__class__.__name__ == "ClassDef":
                #return cur_trans.father.node.name
                return cur_node
    def class_name(self):
        return self.class_node().name
    
    
    def __enter__(self):
        return self.trans()
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass



    def trans(self):
        return eval("self._{}()".format(self.node.__class__.__name__))
    
    def _index_util(self, index_node, fmt):
        index = ""
        with Node_trans(self, index_node) as index_code: index = index_code.strip() 
        if "__" not in index:
            pre_index_line = ""
        else:
            pre_index_line = "ARRAY_INDEX = {index}".format(**locals())
            pre_index_line += Node_trans_cfg.popall_check_after_line_stack(self.type_space)
            index = "ARRAY_INDEX"
        index_line = fmt.format(index=index)
        return pre_index_line, index_line
    def _cond_util(self, cond_node, fmt):
        cond = ""
        with Node_trans(self, cond_node) as cond_code: cond = cond_code.strip() 
        if "__" not in cond:
            pre_cond_line = ""
        else:
            pre_cond_line = "COND_IS_TRUE = ( {cond} )".format(**locals())
            pre_cond_line += Node_trans_cfg.popall_check_after_line_stack(self.type_space)
            cond = "COND_IS_TRUE"
        cond_line = fmt.format(cond=cond)
        return pre_cond_line, cond_line
        # new_stack = []
        # self.type_space.line_stack.append(new_stack)

        # code_list = []
        # with Node_trans(self, cond_node) as cond:
        #     for pre_line in self.type_space.line_stack[-1]:
        #         code_list.append(pre_line)
        #     code_list.append("CASE_IS_TRUE = ( {cond} );".format(**locals()))
        
        # self.type_space.line_stack.pop()
        # code = ";\n".join(code_list)
        # return code

    def _body_util(self, body):
        body_list = []
        for node_stmt in body:
            new_stack = []
            new_stack_after = []
            self.type_space.line_stack.append(new_stack)
            self.type_space.line_stack_after.append(new_stack_after)
            
            with Node_trans(self, node_stmt) as stmt:
                for pre_line in self.type_space.line_stack[-1]:
                    body_list.append(pre_line)
                stmt += Node_trans_cfg.popall_check_after_line_stack(self.type_space)
                body_list.append(stmt)

            self.type_space.line_stack.pop()
            self.type_space.line_stack_after.pop()
        body_list = [line for line in body_list if line.strip()!=""]
        body = self.add_tab(";\n".join(body_list) )
        return body

    def _FunctionDef(self):
        # FunctionDef(identifier name, arguments args,
        #                stmt* body, expr* decorator_list, expr? returns,
        #                string? type_comment)
        node, r = self.node, None
        ##########################################
        
        self.type_space.clear_local()
        #self.type_space.src_clear()
        self.type_space.func_stack = []
        #class_name = self.father.node.name
        deco_list = []
        for decorator in self.node.decorator_list:
            with Node_trans(self, decorator) as deco:
                deco_list.append(deco)
        
        node.args.ext_opt = "func_param"
        name, returns, args = None, None, None
        with Node_trans(self, node.name) as name_code: name=name_code
        with Node_trans(self, node.returns) as returns_code: returns=returns_code 
        with Node_trans(self, node.args) as args_code: args=args_code#arguments
        class_name = self.class_name()

        self.type_space.src.clear_func()
        
        func_code = """
{func_line}
{{
{func_prepare};
{func_stack};

{body};
{free_src};
}}
"""
        func_line = Node_trans_cfg._FunctionDef_proc(class_name, name, returns, args, deco_list, self.type_space)
        self.type_space.src.loop_in()
        body = self._body_util(node.body)

        free_src = self.type_space.src.loop_src_list()
        free_src = self.add_tab( ";\n".join( free_src ))
        self.type_space.src.loop_out()
        
        # if "\n" in body:
        #     last_line = body.rsplit("\n")
        #     if "return " not in last_line:
        #         free_src = Node_trans_cfg.free_src_list(self.type_space)
        
        func_stack = self.add_tab( ";\n".join(self.type_space.func_stack) )
        #Raise = self.add_tab( Node_trans_cfg._Raise_proc("NULL", type_space) )
        func_prepare = self.add_tab( Node_trans_cfg._func_prepare(self.type_space) )
        
        return func_code.format(**locals())
    def _str(self):
        return self.node
    def _Name(self):
        #Name(identifier id, expr_context ctx)
        node, r = self.node, None
        with Node_trans(self, node.id) as id_str:
            r = id_str
        return r
    
    #在这里加入对 local()的本地函数指针的解析。
    def _arguments(self):
        # arguments = (arg* posonlyargs, arg* args, arg? vararg, arg* kwonlyargs,
        #         expr* kw_defaults, arg? kwarg, expr* defaults)
        node, r = self.node, None
        r = []
        for arg_node in node.args:   
            if hasattr(node, "ext_opt"):
                arg_node.ext_opt = node.ext_opt
            with Node_trans(self, arg_node) as arg:
                if "(" in arg:
                    with Node_trans(self, arg_node.arg) as func_name:
                        with Node_trans(self, arg_node.annotation) as func_type:
                            arg = Node_trans_cfg._arguments_proc(func_name, func_type, self.type_space)
                r.append(arg)
        return ", ".join(r)
    def _arg(self):
        # arg = (identifier arg, expr? annotation, string? type_comment)
        #    attributes (int lineno, int col_offset, int? end_lineno, int? end_col_offset)
        node, r = self.node, None
        with Node_trans(self, node.arg) as arg:
            with Node_trans(self, node.annotation) as annotation:
                class_name = self.class_name()
                type_space = self.type_space
                ext_opt = node.ext_opt if hasattr(node, "ext_opt") else None
                # if hasattr(node, "ext_opt") and node.ext_opt == "func_param":
                r = Node_trans_cfg._arg_proc(arg, annotation, class_name, type_space, ext_opt)
        return r
    
    def _Call(self):
        #Call(expr func, expr* args, keyword* keywords)
        node, r = self.node, None
        args = []
        for arg_node in node.args:   
            with Node_trans(self, arg_node) as arg:
                args.append(arg)
        #args =  ", ".join(args)
        with Node_trans(self, node.func) as func:
            r = Node_trans_cfg._Call_proc(func, args, self.type_space)
        return r
    def _NoneType(self): return Node_trans_cfg._NoneType_proc()
    def _Assign(self):
        #Assign(expr* targets, expr value, string? type_comment)
        node, r = self.node, None
        with Node_trans(self, node.targets[0]) as targets:
            with Node_trans(self, node.value) as value:
                r = "{targets} = {value}".format(targets=targets, value=value)
        return r
    def _Attribute(self):
        #Attribute(expr value, identifier attr, expr_context ctx)
        node, r = self.node, None
        with Node_trans(self, node.attr) as attr:
            with Node_trans(self, node.value) as value:
                r = "{value}->{attr}".format(attr=attr, value=value)
        return r
    def _Str(self): 
        #return self.node.__dict__.items()
        s = self.node.s
        type_space = self.type_space
        r = Node_trans_cfg._Str_proc(s, type_space)
        # if s.startswith("##\n"): return ''
        # if s.startswith("#c code\n"): return "\n".join(s.splitlines()[1:])
        # if "\n" in s: return "/*\n{s}\n*/".format(s=s)
        # return '"{s}"'.format(s=s)
        return r
    def _Expr(self):
        #Expr(expr value)
        node, r = self.node, None
        with Node_trans(self, node.value) as value:
            r = "{value}".format(value=value)
        return r
    def _AnnAssign(self):
        #AnnAssign(expr target, expr annotation, expr? value, int simple)
        node, r = self.node, None
        with Node_trans(self, node.target) as target:
            with Node_trans(self, node.annotation) as annotation:
                with Node_trans(self, node.value) as value:
                    r = Node_trans_cfg._AnnAssign_proc(target, annotation, value, self.type_space)
        return r
    def _For(self):
        #For(expr target, expr iter, stmt* body, stmt* orelse, string? type_comment)
        #OOvar(__name__, "For", "")
        node, r = self.node, None
        break_line = ";\n"
        
        self.type_space.src.loop_in()
        body = self._body_util(node.body)
        #body = break_line.join( body )

        body_free = self.type_space.src.loop_src_list()
        body_free = break_line.join( body_free )
        body_free = self.add_tab(body_free)
        self.type_space.src.loop_out()

        body += break_line + body_free
        # body = self._body_util(node.body)
        # free_source = self.add_tab( ";\n".join( self.type_space.src_pop() )  )
        # body = ";\n".join([body, free_source])
        

        obj_name = None 
        with Node_trans(self, node.iter) as obj_name_code: obj_name = obj_name_code
        i = None
        if "enumerate" in obj_name:
            obj_name = obj_name.split("enumerate(")[1].split(")")[0].strip()
            class_name = Node_trans_cfg.ref_to_class_name(self.type_space.get_local_type(obj_name))
            node_tuple = node.target
            #Tuple(expr* elts, expr_context ctx)
            with Node_trans(self, node_tuple.elts[0]) as i:
                with Node_trans(self, node_tuple.elts[1]) as Iter:
                    pass
        else:
            with Node_trans(self, node.target) as Iter:
                pass
        
        r = Node_trans_cfg._For_proc(i, Iter, obj_name, body, self.type_space)
        return r
    def _While(self):
        #While(expr test, stmt* body, stmt* orelse)
        node, r = self.node, None
        break_line = ";\n"


        prev_cond_line, cond_line = self._cond_util(node.test, "if(! ({cond}) ) break;")
        if cond_line == "if(! (1) ) break;":
            cond_line = ""
        prev_cond_line = self.add_tab(prev_cond_line)
        cond_line = self.add_tab(cond_line)
        
        self.type_space.src.loop_in()
        body = self._body_util(node.body)
        #body = break_line.join( body )

        body_free = self.type_space.src.loop_src_list()
        body_free = break_line.join( body_free )
        body_free = self.add_tab(body_free)
        self.type_space.src.loop_out()

    
        # body = self._body_util(node.body)
        # free_source = self.add_tab( ";\n".join( self.type_space.src_pop() )  )
        # body = ";\n".join([body, free_source])
        
        r = """
while(1)
{{
{prev_cond_line}
{cond_line}
{body};
{body_free};
}}""".format(**locals())
        return r
    def _Num(self):
        #.n
        return str(self.node.n)
    def _If(self):
        #If(expr test, stmt* body, stmt* orelse)
        #self.type_space.src_push()
        node, r = self.node, None
        break_line = ";\n"

        prev_cond_line, cond_line = self._cond_util(node.test, "if({cond})")
        #self.type_space.line_stack[-1].append(prev_cond_line)
        self.type_space.src.branch_in()
        body = self._body_util(node.body)
        #body = break_line.join( body )

        body_free = self.type_space.src.branch_src_list()
        body_free = break_line.join( body_free )
        body_free = self.add_tab(body_free)
        self.type_space.src.branch_out()

        
        self.type_space.src.branch_in()
        orelse = self._body_util(node.orelse)
        #orelse = break_line.join( orelse )

        orelse_free = self.type_space.src.branch_src_list()
        orelse_free = break_line.join( orelse_free )
        orelse_free = self.add_tab(orelse_free)
        self.type_space.src.branch_out()
        # free_source = self.add_tab( ";\n".join( self.type_space.src_pop() )  )
        # body = ";\n".join([body, free_source])

        #self.type_space.src_push()
        #case_list =[]
        #self.type_space.src_push()
        
        # free_source = self.add_tab( ";\n".join( self.type_space.src_pop() )  )
        # orelse = ";\n".join([orelse, free_source])
        # for one_case in node.orelse:
        #     with Node_trans(self, one_case) as a_case:
        #         case_list.append(a_case)
        # orelse = self.add_tab( ";\n".join(case_list) )
        
        # if else
        r = """
{prev_cond_line}
{cond_line}
{{
{body};
{body_free};
}}
else
{{
{orelse};
{orelse_free};
}}"""
        if orelse.strip() == "":
            r = """
{prev_cond_line}
{cond_line}
{{
{body};
{body_free};
}}"""
        r = r.format(**locals())

        return r
    def _IfExp(self):
        #IfExp(expr test, expr body, expr orelse)
        node, r = self.node, None
        with Node_trans(self, node.test) as test:
            with Node_trans(self, node.orelse) as orelse:
                with Node_trans(self, node.body) as body:
                    #body = self._body_util(node.body)
                    r = "{test} ? {body} : {orelse}".format(**locals())
        return r
    def _BoolOp(self):
        #BoolOp(boolop op, expr* values)
        node, r = self.node, None
        with Node_trans(self, node.op) as op:
            r = []
            for value_node in node.values:
                with Node_trans(self, value_node) as value:
                    r.append(value)
            return " {op} ".format(op=op).join(r)
        return "COMPILE ERROR:: BOOLOP......."
    def _And(self): return "&&"
    def _Compare(self):
        #Compare(expr left, cmpop* ops, expr* comparators)
        node, r = self.node, None
        with Node_trans(self, node.ops[0]) as ops:
            with Node_trans(self, node.left) as left:
                with Node_trans(self, node.comparators[0]) as comparators:
                    r = "{left} {ops} {comparators}"
                    r = r.format(ops=ops, left=left, comparators=comparators)
        return r
    def _Return(self): 
        #Return(expr? value)
        node, r = self.node, None
        value = None
        if hasattr(node, "value"):
            with Node_trans(self, node.value) as value:
                pass
        r = Node_trans_cfg._Return_proc(value, self.type_space)
        return r

    def _Continue(self): return Node_trans_cfg._Continue_proc(self.type_space)
    def _Break(self): return Node_trans_cfg._Break_proc(self.type_space)

    def _LtE(self): return "<="
    def _Pass(self): return Node_trans_cfg._Pass_proc()
    def _Mod(self): return "%"
    def _NotEq(self): return "!="
    def _Lt(self): return "<"
    def _Not(self): return "!"
    def _BitOr(self): return "|_BitOr"
    def _Or(self): return "||"
    def _Add(self): return "+"
    def _Sub(self): return "-"
    def _Div(self): return "/"
    def _Mult(self): return "*"
    def _Eq(self): return "=="
    def _GtE(self): return ">="
    def _Gt(self): return ">"
    def _USub(self): return "-"
    def _BinOp(self):
        #BinOp(expr left, operator op, expr right)
        node, r = self.node, None
        with Node_trans(self, node.op) as op:
            with Node_trans(self, node.left) as left:
                with Node_trans(self, node.right) as right:
                    r = "{left} {op} {right}"
                    if op == ".":
                        r = "{left}{op}{right}"
                    r = r.format(op=op, left=left, right=right)
        return r
    def _UnaryOp(self):
        # -333 
        # op: '-'  operand '333'
        #UnaryOp(unaryop op, expr operand)
        node, r = self.node, None
        with Node_trans(self, node.op) as op:
            with Node_trans(self, node.operand) as operand:
                r = "{op}{operand}".format(op=op, operand=operand)
        return r
    
    def _Tuple(self):
        #Tuple(expr* elts, expr_context ctx)
        node, r = self.node, None
        with Node_trans(self, node.elts) as elts:
            r = elts
        return r
    def _Starred(self):
        #Starred(expr value, expr_context ctx)
        node, r = self.node, None
        with Node_trans(self, node.value) as value:
            r = "*"+value
        return r
    def _NameConstant(self): return "NameConstant[{}]".format(str(self.node))
    def _Subscript(self):
        # Subscript(expr value, slice slice, expr_context ctx)
        node, r = self.node, None
        value, Slice = None, None
        with Node_trans(self, node.value) as value_code: value=value_code
        #with Node_trans(self, node.slice) as Slice_code: Slice = Slice_code
        pre_index_line, index_line = self._index_util(node.slice, "{value}[{{index}}]".format(**locals()))
        self.type_space.line_stack[-1].append(pre_index_line)
        r = """{index_line}""".format(**locals())
        # r = "{value}[{Slice}]".format(**locals())
        # if ";" in Slice:
        #     self.type_space.line_stack[-1].append("ARRINDEX = {Slice};".format(**locals()))
        #     r = """{value}[ARRINDEX]""".format(**locals())
        #r = "Subscript--"+value+slice_
        return r
    def _Index(self):
        # _Subscript.slice
        # Index(expr value)
        node, r = self.node, None
        with Node_trans(self, node.value) as value:
            r = value
        return r
    
    
    # # + --> *
    # def _UAdd(self): return "*"

    def _AugAssign(self):
        #AugAssign(expr target, operator op, expr value)
        node, r = self.node, None
        with Node_trans(self, node.target) as target:
            with Node_trans(self, node.op) as op:
                with Node_trans(self, node.value) as value:
                    # x += 1
                    r = "{target} {op}= {value}".format(**locals())
        return r
    def _List(self):
        # List(expr* elts, expr_context ctx)
        node, r = self.node, None
        with Node_trans(self, node.elts) as elts:
            r = elts
        return r
    def _Dict(self):
        #Dict(expr* keys, expr* values)
        node, r = self.node, {}
        for i in range(len(node.keys)):
            with Node_trans(self, node.keys[i]) as key:
                with Node_trans(self, node.values[i]) as value:
                    r[key] = value
        return str(r)
    
    def _With(self):
        #With(withitem* items, stmt* body, string? type_comment)
        # with {context_expr} as {var}:
        #   {body}
        
        tab_and_quote = lambda s: self.add_tab( "\n".join( [line+";" for line in s.splitlines()] ) )

        item = self.node.items[0]
        opt = []
        with Node_trans(self, item.context_expr) as opt_maker:
            with Node_trans(self, item.optional_vars) as opt_param:
                opt = opt_maker(opt_param)
        var, _enter, _cond, _exit, _error_proc = opt

        tab_exit = tab_and_quote(_exit )
        with Node_trans(self, self.node.body) as body:
            tab_body = self.add_tab( body) + ";"
            r = """
{var} = {_enter};
if ({_cond})
{{
{tab_body}
{tab_exit}
}}
""".format(**locals())
            if len(_error_proc) > 0:
                tab_error_proc = tab_and_quote( _error_proc )
                r += """else
{{
{tab_error_proc}
}}
""".format(**locals())
        return r
        
    def _Raise(self):
        #Raise(expr? exc, expr? cause)

        r = None
        with Node_trans(self, self.node.exc) as value:
            r = Node_trans_cfg._Raise_proc(value, self.type_space)
        return r
    def _BitXor(self): return "."
